# * Rick Hull
# * Genesis: 2006-02-01

#
# additional methods for Ruby core modules, classes, and instances are below
#

class Object
  # shows which class defines a method
  #
  def method_sites
    width = self.methods.col_width + 1
    self.methods.sort.map { |m|
      [m.to_s.ljust(width, ' '),
       (self.method(m).to_s.match(/\((.*)\)/)[1] rescue self.class)].join
    }
  end

  def deep_clone
    Marshal.load(Marshal.dump(self))
  end
end

class String
  def chop_front(num_chars = 1)
    num_chars = num_chars.to_i
    warn "undefined behavior for #{num_chars}" if num_chars < 0
    self[num_chars..-1]
  end

  def chop_rear(num_chars = 1)
    num_chars = num_chars.to_i
    warn "undefined behavior for #{num_chars}" if num_chars < 0
    self[0..(-1*(num_chars + 1))]
  end
end

class Hash
  # raise exception if p is not a existing key
  #
  def check(p, exception_class = RuntimeError)
    raise(exception_class, "missing param: #{p}") if !self.key?(p)
  end
end

class Array
  # if #first is car, this is cdr
  #
  def rest
    self[1..-1]
  end

  # returns the length of the longest object, based on meth (default: to_s)
  #
  def col_width(meth = :to_s)
    self.map { |obj| obj.send(meth).size }.max
  end

  # returns the largest object
  #
  def largest(meth = :to_s)
    tmp = nil
    self.each { |obj| tmp = obj if obj.send(meth).size > tmp.send(meth).size }
    tmp
  end
end


# statistics methods
#
module Enumerable
  def sum
    return self.inject(0) { |acc, i| acc + i }
  end
  
  def average
    return self.sum / self.length.to_f
  end
  
  def sample_variance
    avg = self.average
    sum = self.inject(0) { |acc, i| acc + (i - avg) ** 2 }
    return (1 / self.length.to_f * sum)
  end
  
  def standard_deviation
    return Math.sqrt(self.sample_variance)
  end

  # return a random element
  def rand
    a = self.to_a
    a[super(a.size)]
  end
end

class Exception
  # formatted string to show relevant exception details
  #
  def details
    [[self.class, self].join(': '), 
     self.backtrace.join("\n")].join("\n")
  end
end

class Numeric
  def even?
    self.modulo(2).zero?
  end
end
